﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Lucene.Net.Documents;
using Lucene.Net.Store;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Lucene.Net.Index;
using Lucene.Net.Analysis.Standard;
using Version = Lucene.Net.Util.Version;

namespace SPSearch.LuceneIndexing.SiteIndexer
{
    static class Program
    {
        private static int statusLine;

        static void Main(string[] args)
        {
            if(args.Length != 1 
                || !Uri.IsWellFormedUriString(args[0], UriKind.Absolute) 
                || !SPSite.Exists(new Uri(args[0])))
            {
                Console.WriteLine("No Site Url specified or invalid/does not exist");
                Console.WriteLine("Rerun this app with the single argument: <absolute site url>");
                return;
            }

            var webApplication = SPWebApplication.Lookup(new Uri(args[0]));
            var indexPath = webApplication.IisSettings[SPUrlZone.Default].Path
                + @"\Resources\LuceneIndex";
            var indexWriter = new IndexWriter(
                FSDirectory.Open(new DirectoryInfo(indexPath)), 
                new StandardAnalyzer(Version.LUCENE_CURRENT),
                IndexWriter.MaxFieldLength.UNLIMITED);
            
            Console.WriteLine("Beginning Indexing...");
            statusLine = Console.CursorTop;
            Console.CursorVisible = false;
            var startTime = DateTime.Now;

            foreach (var site in webApplication.Sites.Cast<SPSite>().OrderBy(s => s.Url))
                IndexWebAndSubWebs(indexWriter, site.ServerRelativeUrl, site.OpenWeb());

            indexWriter.Optimize();
            indexWriter.Close();
            WriteConsoleLine("Indexing Complete. Total time taken (Seconds): " + DateTime.Now.Subtract(startTime).TotalSeconds);
            Console.CursorVisible = true;

            if (!Debugger.IsAttached) return;
            Console.ReadKey(true);
        }

        private static void IndexWebAndSubWebs(IndexWriter indexWriter, string siteUrl, SPWeb web)
        {
            foreach (var list in web.Lists.Cast<SPList>().Where(l => !l.Hidden).OrderBy(l => l.Title))
            {
                WriteConsoleLine("Indexing {0}{1}",
                    web.ServerRelativeUrl, list.Title);
                foreach (var item in list.GetItems(new SPQuery()).Cast<SPListItem>()
                    .Where(i => i.ModerationInformation == null 
                        || i.ModerationInformation.Status.Equals("Approved")))
                        indexWriter.AddDocument(DocumentFrom(item, siteUrl, web, list));
            }

            foreach (var subWeb in web.Webs.Cast<SPWeb>().OrderBy(w => w.Name))
                IndexWebAndSubWebs(indexWriter, siteUrl, subWeb);
        }

        private static Document DocumentFrom(SPListItem item, string siteUrl, SPWeb web, SPList list)
        {
            var document = new Document();

            document.Add(new Field("SiteUrl", siteUrl, Field.Store.YES, Field.Index.ANALYZED));
            document.Add(new Field("WebID", web.ID.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.Add(new Field("WebTitle", web.Title, Field.Store.YES, Field.Index.ANALYZED));
            document.Add(new Field("WebUrl", web.Url, Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.Add(new Field("ListID", list.ID.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.Add(new Field("ListTitle", list.Title, Field.Store.YES, Field.Index.ANALYZED));
            document.Add(new Field("ItemUrl", 
                string.Format("{0}/{1}?ID={2}", web.Url, list.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url, item.ID), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.Add(new Field("ContentType", item.ContentType.Name, Field.Store.YES, Field.Index.NOT_ANALYZED));

            var roleAssignmentIDs = string.Empty;
            var roleAssignmentNames = string.Empty;
            foreach (var principal in item.RoleAssignments.Cast<SPRoleAssignment>().Select(m => m.Member))
            {
                roleAssignmentIDs += principal.ID + ",";
                roleAssignmentNames += principal.Name + ",";
            }
            document.Add(new Field("RoleAssignmentIDs", roleAssignmentIDs, Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.Add(new Field("RoleAssignmentNames", roleAssignmentNames, Field.Store.YES, Field.Index.NOT_ANALYZED));

            foreach (var field in item.Fields.Cast<SPField>()
                .Where(f => !f.Hidden && item[f.InternalName] != null)
                .OrderBy(f => f.Title))
                document.Add(new Field(field.Title, item[field.InternalName].ToString(), Field.Store.YES, Field.Index.ANALYZED));

            return document;
        }

        private static void WriteConsoleLine(string textToFormat, params object[] parameters)
        {
            Console.CursorLeft = 0;
            if(Console.CursorTop != statusLine)
                Console.WriteLine(new string(' ', Console.WindowWidth));
            Console.CursorTop = statusLine;

            var text = string.Format(textToFormat, parameters);
            if(text.Length < Console.WindowWidth)
                text += new string(' ', Console.WindowWidth - text.Length);
            Console.Write(text);
        }
    }
}
